<!DOCTYPE html>
<html>
<head>
<title>RTCPeerConnection.getSenders</title>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<script>
promise_test(async t => {
  let pc = new RTCPeerConnection();
  t.add_cleanup(() => pc.close());
  return createStreams({audio:true, video:false}, 1)
    .then(function(streams) {
      let stream = streams[0];
      let track = stream.getAudioTracks()[0];
      let sender = pc.addTrack(track);
      assert_equals(sender.track, track);
      assert_array_equals(pc.getLocalStreams(), []);
    });
}, 'addTrack() for a single track.');

promise_test(async t => {
  let pc = new RTCPeerConnection();
  t.add_cleanup(() => pc.close());
  return createStreams({audio:true, video:false}, 1)
    .then(function(streams) {
      let stream = streams[0];
      let track = stream.getAudioTracks()[0];
      let sender = pc.addTrack(track, stream);
      assert_equals(sender.track, track);
      assert_array_equals(pc.getLocalStreams(), [ stream ]);
    });
}, 'addTrack() for a single track and its stream.');

promise_test(async t => {
  let pc = new RTCPeerConnection();
  t.add_cleanup(() => pc.close());
  return createStreams({audio:true, video:false}, 2)
    .then(function(streams) {
      let streamA = streams[0];
      let trackA = streamA.getAudioTracks()[0];
      let streamB = streams[1];
      let sender = pc.addTrack(trackA, streamB);
      assert_equals(sender.track, trackA);
      assert_array_equals(pc.getLocalStreams(), [ streamB ]);
    });
}, 'addTrack() for a single track and a different stream.');

promise_test(async t => {
  let pc = new RTCPeerConnection({sdpSemantics: "plan-b"});
  t.add_cleanup(() => pc.close());
  let [streamA, streamB] = await createStreams({audio:true, video:false}, 2);
  let track = streamA.getAudioTracks()[0];
  assert_throws_dom('NotSupportedError', () => {
    pc.addTrack(track, streamA, streamB)
  });
}, 'addTrack() for a single track and two streams (Plan B) throws NotSupportedError.');

promise_test(async t => {
  let pc = new RTCPeerConnection({sdpSemantics: "unified-plan"});
  t.add_cleanup(() => pc.close());
  let [streamA, streamB] = await createStreams({audio:true, video:false}, 2);
  let track = streamA.getAudioTracks()[0];
  let sender =  pc.addTrack(track, streamA, streamB);
  assert_equals(sender.track, track);
  assert_array_equals(pc.getLocalStreams(), [ streamA, streamB ]);
}, 'addTrack() for a single track and two streams (Unified Plan).');

promise_test(async t => {
  let pc = new RTCPeerConnection();
  t.add_cleanup(() => pc.close());
  return createStreams({audio:true, video:false}, 1)
    .then(function(streams) {
      let stream = streams[0];
      let track = stream.getAudioTracks()[0];
      let sender = pc.addTrack(track);
      assert_equals(sender.track, track);
      let exception = null;
      try {
        pc.addTrack(track);
      } catch (e) {
        exception = e;
      }
      assert_equals('InvalidAccessError', exception.name);
    });
}, 'addTrack() when already added throws InvalidAccessError.');

promise_test(async t => {
  let pc = new RTCPeerConnection();
  t.add_cleanup(() => pc.close());
  return createStreams({audio:true, video:false}, 1)
    .then(function(streams) {
      let stream = streams[0];
      let track = stream.getAudioTracks()[0];
      pc.addStream(stream);
      let exception = null;
      try {
        // The interaction between |addStream| and |addTrack| is not
        // standardized (|addStream| is non-standard). Because |addStream|
        // creates a sender for the track and |addTrack| throws if there already
        // exists a sender for the track.
        pc.addTrack(track);
      } catch (e) {
        exception = e;
      }
      assert_true(exception != null);
      assert_equals('InvalidAccessError', exception.name);
    });
}, 'addTrack() after addStream() throws InvalidAccessError.');

promise_test(async t => {
  let pc = new RTCPeerConnection();
  t.add_cleanup(() => pc.close());
  return createStreams({audio:true, video:false}, 1)
    .then(function(streams) {
      let stream = streams[0];
      let track = stream.getTracks()[0];
      let sender = pc.addTrack(track);
      assert_equals(sender.track, track);
      assert_array_equals(pc.getSenders(), [ sender ]);
      assert_array_equals(pc.getLocalStreams(), []);
      // This is a NO-OP because there already is a sender for this track.
      pc.addStream(stream);
      // The sender is not associated with the stream, so |addStream| has no
      // effect in this case. Otherwise it would already have been part of
      // getLocalStreams().
      assert_array_equals(pc.getLocalStreams(), []);
      assert_array_equals(pc.getSenders(), [ sender ]);
    });
}, 'addTrack() before addStream() works.');

promise_test(async t => {
  let pc = new RTCPeerConnection();
  t.add_cleanup(() => pc.close());
  return createStreams({audio:true, video:true}, 1)
    .then(function(streams) {
      let stream = streams[0];
      let track = stream.getTracks()[0];
      let sender = pc.addTrack(track);
      assert_equals(sender.track, track);
      assert_array_equals(pc.getSenders(), [ sender ]);
      assert_array_equals(pc.getLocalStreams(), []);
      // This should add the remaining track, creating a sender associated with
      // the stream.
      pc.addStream(stream);
      assert_array_equals(pc.getLocalStreams(), [ stream ]);
      assert_equals(pc.getSenders().length, 2);
      assert_true(pc.getSenders().find(
          s => { return s.track == stream.getTracks()[1]; }) != null);
    });
}, 'addStream() after addTrack() works if there are more tracks to be added.');

/**
 * Helper functions.
 */

function createStreams(constraints, numStreams, streamsSoFar = []) {
  if (numStreams == 0) {
    return Promise.resolve(streamsSoFar);
  }
  return navigator.mediaDevices.getUserMedia(constraints)
    .then(function(stream) {
      return createStreams(constraints,
                           numStreams - 1,
                           streamsSoFar.concat([stream]));
    });
}
</script>
</body>
</html>
